package cn.demoncat.util.mybatis.base;

import cn.demoncat.util.exception.BizRuntimeException;
import cn.demoncat.util.exception.ParamRuntimeException;
import cn.demoncat.util.lang.IDUtil;
import cn.demoncat.util.lang.ParamEmptyUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.entity.MapSo;
import cn.demoncat.util.lang.entity.Result;
import cn.demoncat.util.mybatis.constant.MapperConstant;
import cn.demoncat.util.mybatis.util.MapperUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.*;
import java.util.function.Consumer;

/**
 * 描述：树型数据BaseServiceImpl
 *
 * @author 延晓磊
 *
 * @since 2017年3月26日
 */
public abstract class BaseTreeServiceImpl<T extends BaseTreeEntity<T, P>, P extends Serializable> extends BaseServiceImpl<T, P>
		implements BaseTreeService<T, P> {

	/**
	 * 初始化DAO
	 */
	protected abstract BaseTreeDao<T, P> getDao();

	// 乐观锁操作 - 新增时递增父节点的subCount
	private final Consumer<T> addTask = t -> t.setSubCount(t.getSubCount() + 1);
	// 乐观锁操作 - 删除时递减父节点的subCount
	private final Consumer<T> subTask = t -> t.setSubCount(t.getSubCount() - 1);

	/**
	 * 获取名称
	 * 
	 * @param id	ID
	 * 
	 * @return [父,子]
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年1月17日
	 */
	@Override
	public List<String> findName(P id){
		return findName(null, id);
	}
	
	/**
	 * 获取名称集
	 * 
	 * @param ids	ID集
	 * 
	 * @return {id : [父,子]}
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年1月17日
	 */
	@Override
	public Map<P,List<String>> findNames(Collection<P> ids){
		Map<P,List<String>> map = new HashMap<>();
		if (CollectionUtils.isEmpty(ids)) {
			return map;
		}
		for (P id : ids) {
			map.put(id, findName(id));
		}
		return map;
	}
	
	/**
	 * 获取子节点数
	 * 
	 * @param id
	 */
	@Override
	public long getSubCount(P id) {
		return countAll(MapSo.getInstance(MapperConstant.FieldName.PARENT_ID, id));
	}
	
	/**
	 * 获取子节点
	 * 
	 * @param id
	 */
	@Override
	public List<T> getChildren(P id) {
		return findAll(MapSo.getInstance(MapperConstant.FieldName.PARENT_ID, id));
	}
	
	/**
	 * 获取子节点
	 * 
	 * @param id
	 * @param sort	排序方式
	 */
	@Override
	public List<T> getChildren(P id, String sort) {
		return findAll(MapSo.getInstance(MapperConstant.FieldName.PARENT_ID, id), sort);
	}
	
	/**
	 * 变更节点的subCount
	 * 
	 * @param id
	 * @param add	true递增，false递减
	 * 
	 * 注意：事务须设置为READ_COMMITTED，否则更新节点使用乐观锁时无法重试
	 */
	@Override
	public void updSubCount(P id, boolean add) {
		if (id == null || id.toString().equals(IDUtil.ROOT_ID)) {
			return;
		}
		if (add) {
			super.update(id, addTask);
		}else {
			super.update(id, subTask);
		}
	}
	
	/**
	 * 树型插入
	 * 
	 * 1、插入时校验 o.parentId和name非空
	 * 2、判断sort为空时，设置默认sort
	 * 
	 * @param o	对象
	 * 
	 * @return 成功返回1，失败返回0，参数有问题抛异常
	 */
	@Override
	public int saveTree(T o) {
		return saveTree(o, false);
	}
	
	/**
	 * 树型插入，CAS修改父节点的subCount
	 * 
	 * 1、插入时校验 o.parentId和name非空
	 * 2、判断sort为空时，设置默认sort
	 * 3、插入成功时，CAS方式修改父节点的subCount
	 * 
	 * 注意：事务须设置为READ_COMMITTED，否则更新父节点使用乐观锁时无法重试
	 * 
	 * @param o	对象
	 * 
	 * @return 成功返回1，失败返回0，参数有问题抛异常
	 */
	@Override
	public int saveTreeCas(T o) {
		return saveTree(o, true);
	}
	
	/**
	 * 树型更新
	 * 
	 * 1、排除null属性的字段
	 * 2、如果o或id为null，抛异常
	 * 3、禁止name为空
	 * 4、禁止修改sort,parentId,nodeLevel,subCount字段
	 * 
	 * @param o
	 * @return 修改记录数
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年8月1日
	 */
	@Override
	public int updateTree(T o) {
		if (o == null || o.getId() == null || StringUtils.isBlank(o.getName())) {
			throw ParamRuntimeException.NULL;
		}
		// 禁止修改部分属性
		o.setNodeLevel(null);
		o.setParentId(null);
		o.setSort(null);
		o.setSubCount(null);
		return this.getDao().update(o);
	}

	/**
	 * 树型条件更新
	 * 
	 * 1、排除null属性的字段
	 * 2、如果o或id为null，抛异常
	 * 3、如果o.id为null，将更新condition匹配的所有数据；如果o.id非null，将判断id对应的数据是否满足condition
	 * 4、禁止name为空
	 * 5、禁止修改sort,parentId,nodeLevel,subCount字段
	 * 
	 * @param o
	 * @param condition	条件
	 * @return 修改记录数
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年8月1日
	 */
	@Override
	public int updateTree(T o, Map<String, Object> condition) {
		if (o == null || StringUtils.isBlank(o.getName())) {
			throw ParamRuntimeException.NULL;
		}
		// 禁止修改部分属性
		o.setNodeLevel(null);
		o.setParentId(null);
		o.setSort(null);
		o.setSubCount(null);
		// 条件更新
		Map<String, Object> params = new HashMap<>(2);
		params.put(MapperConstant.ParamName.OBJ, o);
		params.put(MapperConstant.ParamName.CONDITION, condition);
		return this.getDao().updateByCondition(params);
	}

	/**
	 * 根据ID删除
	 *
	 * @param id
	 * @return 删除的数据，数据不存在/删除失败返回null，级联抛异常
	 */
	@Override
	public T deleteAndGetTree(P id) {
		return deleteAndGetTree(id, null);
	}

	/**
	 * 根据ID删除
	 *
	 * @param id
	 * @param msg	级联的异常消息
	 * @return 删除的数据，数据不存在/删除失败返回null，级联抛异常
	 */
	@Override
	public T deleteAndGetTree(P id, String msg) {
		// 查询
		T o = this.getById(id);
		if (o != null) {
			// 级联校验
			if(super.countAll(MapSo.getInstance(MapperConstant.FieldName.PARENT_ID, id)) > 0) {
				throw new BizRuntimeException(StringUtil.toDefault(msg, Result.ERROR_DELETE_CHILDREN.getMsg()));
			}
			// 删除
			Map<String, Object> condition = new HashMap<>(1);
			condition.put(MapperConstant.FieldName.ID, id);
			if (this.getDao().delete(condition) > 0) {
				return o;
			}
		}
		return null;
	}

	/**
	 * 树型删除
	 * 
	 * 1、查询数据是否存在：不存在返回0
	 * 2、校验子节点：有子节点返回-1
	 * 3、删除：数据存在返回1，数据不存在返回0
	 * 
	 * @param id
	 * 
	 * @return 成功返回1，不存在返回0，有子节点返回-1
	 */
	@Override
	public int deleteTree(P id) {
		return deleteTree(id, null, false);
	}
	
	/**
	 * 树型删除，CAS修改父节点的subCount
	 * 
	 * 1、查询数据是否存在：不存在返回0
	 * 2、校验子节点：有子节点返回-1
	 * 3、删除：数据存在返回1，数据不存在返回0
	 * 4、CAS修改父节点的subCount：返回1
	 * 
	 * 注意：事务须设置为READ_COMMITTED，否则更新父节点使用乐观锁时无法重试
	 * 
	 * @param id
	 * 
	 * @return 成功返回1，不存在返回0，有子节点返回-1
	 */
	@Override
	public int deleteTreeCas(P id) {
		return deleteTree(id, null, true);
	}
	
	/**
	 * 树型删除，校验结果
	 * 
	 * 1、校验数据不存在：0/抛异常
	 * 2、校验有子节点：-1/抛异常
	 * 
	 * @param id
	 * @param condition
	 * @param canNull		允许数据不存在：true返回0，false抛异常
	 * @param canHasChild	允许有子节点：true返回-1，false抛异常
	 * @param childErrMsg	有子节点时的错误信息，为空时默认消息“请先删除子节点”
	 * 
	 * @return 成功返回1，有子节点时返回-1或异常，数据不存在返回0或异常
	 */
	@Override
	public int deleteTreeVali(P id, Map<String, Object> condition, boolean canNull, boolean canHasChild, String childErrMsg) {
		return deleteTreeVali(id, condition, false, canNull, canHasChild, childErrMsg);
	}
	
	/**
	 * 树型删除，校验结果，CAS修改父节点的subCount
	 * 
	 * 1、校验数据不存在：0/抛异常
	 * 2、校验有子节点：-1/抛异常
	 * 3、CAS修改父节点的subCount：返回1
	 * 
	 * 注意：事务须设置为READ_COMMITTED，否则更新父节点使用乐观锁时无法重试
	 * 
	 * @param id
	 * @param condition
	 * @param canNull		允许数据不存在：true返回0，false抛异常
	 * @param canHasChild	允许有子节点：true返回-1，false抛异常
	 * @param childErrMsg	有子节点时的错误信息，为空时默认消息“请先删除子节点”
	 * 
	 * @return 成功返回1，有子节点时返回-1或异常，数据不存在返回0或异常
	 */
	@Override
	public int deleteTreeValiCas(P id, Map<String, Object> condition, boolean canNull, boolean canHasChild, String childErrMsg) {
		return deleteTreeVali(id, condition, true, canNull, canHasChild, childErrMsg);
	}
	
	/**
	 * 树型删除，校验结果
	 * 
	 * @param id
	 * @param condition
	 * @param childErrMsg	有子节点时的错误信息，为空时默认消息“请先删除子节点”
	 * 
	 * @return 成功返回1
	 * 
	 * @throws BizRuntimeException 数据不存在，有子节点
	 */
	@Override
	public int deleteTreeVali(P id, Map<String, Object> condition, String childErrMsg) {
		return deleteTreeVali(id, condition, false, false, false, childErrMsg);
	}
	
	/**
	 * 树型删除，校验结果，CAS修改父节点的subCount
	 * 
	 * 注意：事务须设置为READ_COMMITTED，否则更新父节点使用乐观锁时无法重试
	 * 
	 * @param id
	 * @param condition
	 * @param childErrMsg	有子节点时的错误信息，为空时默认消息“请先删除子节点”
	 * 
	 * @return 成功返回1
	 * 
	 * @throws BizRuntimeException 数据不存在，有子节点
	 */
	@Override
	public int deleteTreeValiCas(P id, Map<String, Object> condition, String childErrMsg) {
		return deleteTreeVali(id, condition, true, false, false, childErrMsg);
	}
	
	/**
	 * 树型删除，校验结果
	 * 
	 * @param id
	 * @param childErrMsg	有子节点时的错误信息，为空时默认消息“请先删除子节点”
	 * 
	 * @return 成功返回1
	 * 
	 * @throws BizRuntimeException 数据不存在，有子节点
	 */
	@Override
	public int deleteTreeVali(P id, String childErrMsg) {
		return deleteTreeVali(id, null, false, false, false, childErrMsg);
	}
	
	/**
	 * 树型删除，校验结果，CAS修改父节点的subCount
	 * 
	 * 注意：事务须设置为READ_COMMITTED，否则更新父节点使用乐观锁时无法重试
	 * 
	 * @param id
	 * @param childErrMsg	有子节点时的错误信息，为空时默认消息“请先删除子节点”
	 * 
	 * @return 成功返回1
	 * 
	 * @throws BizRuntimeException 数据不存在，有子节点
	 */
	@Override
	public int deleteTreeValiCas(P id, String childErrMsg) {
		return deleteTreeVali(id, null, true, false, false, childErrMsg);
	} 
	
	/**
	 * 树型删除，校验结果
	 * 
	 * @param id
	 * 
	 * @return 成功返回1
	 * 
	 * @throws BizRuntimeException 数据不存在，有子节点
	 */
	@Override
	public int deleteTreeVali(P id) {
		return deleteTreeVali(id, null, false, false, false, null);
	}
	
	/**
	 * 树型删除，校验结果，CAS修改父节点的subCount
	 * 
	 * 注意：事务须设置为READ_COMMITTED，否则更新父节点使用乐观锁时无法重试
	 * 
	 * @param id
	 * 
	 * @return 成功返回1
	 * 
	 * @throws BizRuntimeException 数据不存在，有子节点
	 */
	@Override
	public int deleteTreeValiCas(P id) {
		return deleteTreeVali(id, null, true, false, false, null);
	} 
	
	/**
	 * 获取名称
	 * 
	 * @param list	链表
	 * @param id	ID
	 * 
	 * @return [父,子]
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年1月17日
	 */
	private List<String> findName(LinkedList<String> list, P id) {
		// 入口
		if (list == null) {
			list = new LinkedList<>();
		}
		// 出口
		if (ParamEmptyUtil.isEmpty(id) || id.toString().startsWith(IDUtil.ROOT_ID)) {
			return list;
		}
		// 查询
		T o = getById(id);
		// 出口
		if (o == null) {
			return list;
		}
		// 保存名称
		list.addFirst(o.getName());
		// 递归
		return findName(list, o.getParentId());
	}
	
	/**
	 * 树型插入
	 * 
	 * 1、插入时校验 o.parentId和name非空
	 * 2、判断sort为空时，设置默认sort
	 * 3、插入成功时，CAS方式修改父节点的subCount（subCount = true）
	 * 
	 * 注意：如果subCount=true，事务须设置为READ_COMMITTED，否则更新父节点使用乐观锁时无法重试
	 * 
	 * @param o	对象
	 * @param subCount	是否递增父节点subCount
	 * 
	 * @return 成功返回1，失败返回0，参数有问题抛异常
	 */
	private int saveTree(T o, boolean subCount) {
		// 参数校验
		if (o.getParentId() == null || StringUtils.isBlank(o.getName())) {
			throw ParamRuntimeException.NULL;
		}
		// 默认值
		o.setSubCount(0);
		if (o.getSort() == null) {
			o.setSort(System.currentTimeMillis());
		}
		// 插入
		int re = super.save(o);
		// CAS递增父节点的子节点数
		if(subCount) {
			if (re > 0 && !IDUtil.ROOT_ID.equals(o.getParentId().toString())) {
				super.update(o.getParentId(), addTask);
			}
		}
		return re;
	}
	
	/**
	 * 树型删除
	 * 
	 * 1、查询数据是否存在（subCount = true）：不存在返回0
	 * 2、校验子节点：有子节点返回-1
	 * 3、删除：数据存在返回1，数据不存在返回0
	 * 4、CAS修改父节点的subCount（subCount = true）：返回1
	 * 
	 * 注意：如果subCount=true，事务须设置为READ_COMMITTED，否则更新父节点使用乐观锁时无法重试
	 * 
	 * @param id
	 * @param condition
	 * @param subCount	是否递减父节点subCount	
	 * 
	 * @return 成功返回1，不存在返回0，有子节点返回-1
	 */
	private int deleteTree(P id, Map<String, Object> condition, boolean subCount) {
		// 查询数据
		T o = null;
		if(subCount) {
			o = super.getById(id, condition);
			if (o == null) {
				return 0;
			}
		}
		// 判断子节点数
		if(super.countAll(MapSo.getInstance(MapperConstant.FieldName.PARENT_ID, id)) > 0) {
			return -1;
		}
		// 删除
		int i = this.delete(id);
		if (subCount && i > 0) {
			// CAS递减父节点的子节点数
			if (!IDUtil.ROOT_ID.equals(o.getParentId().toString())) {
				super.update(o.getParentId(), subTask);
			}
		}
		return i;
	}
	
	/**
	 * 树型删除，校验结果
	 * 
	 * 1、校验数据不存在：0/抛异常
	 * 2、校验有子节点：-1/抛异常
	 * 3、CAS修改父节点的subCount（subCount = true）：返回1
	 * 
	 * 注意：如果subCount=true，事务须设置为READ_COMMITTED，否则更新父节点使用乐观锁时无法重试
	 * 
	 * @param id
	 * @param condition
	 * @param subCount		是否递减父节点subCount	
	 * @param canNull		允许数据不存在：true返回0，false抛异常
	 * @param canHasChild	允许有子节点：true返回-1，false抛异常
	 * @param childErrMsg	有子节点时的错误信息，为空时默认消息“请先删除子节点”
	 * 
	 * @return 成功返回1，有子节点时返回-1或异常，数据不存在返回0或异常
	 */
	private int deleteTreeVali(P id, Map<String, Object> condition, boolean subCount, boolean canNull, boolean canHasChild, String childErrMsg) {
		return MapperUtil.valiDeleteTreeResult(deleteTree(id, condition, subCount), canNull, canHasChild, childErrMsg);
	}
}
